/*
 * PhotonRTOS础光实时操作系统 -- 资源
 *
 * Copyright (C) 2022, 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Baoyou Xie <xiebaoyou@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <autosar/internal.h>

/* 天花板协议中配置每个资源的最大优先级 */
VAR(struct osek_resource, AUTOMATIC) osek_resources[OS_NR_RESOURCE] = {
	{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}
};

LOCAL_INLINE FUNC(int32_t, OS_CODE) prio_of_resID(
	VAR(ResourceType, AUTOMATIC) ResID)
{
	return osek_resources[ResID].prio;
}

static FUNC(void, OS_CODE) __osek_get_resource(
	VAR(ResourceType, AUTOMATIC) ResID)
{
	/**
	 * 将任务调度优先级提升到资源优先级
	 */
	if (osek_tasks[osek_current_id()].sched_prio < prio_of_resID(ResID)) {
		/**
		 * 在系统生成时，为每个资源静态分配其自己的最高优先级。
		 * 上限优先级应至少设置为访问资源的
		 * 所有任务或链接到该资源的
		 * 任何资源的最高优先级。
		 * 上限优先级应低于所有不访问该资源的
		 * 任务的最低优先级，
		 * 并且其优先级高于所有访问该资源的任务的
		 * 最高优先级。
		 * 综上所述，通过提升优先级可以
		 * 确保单核互斥访问资源，
		 * 而不需要锁机制
		 */
		osek_task_change_prio(osek_current_id(), prio_of_resID(ResID));
	}

	/**
	 * 设置标志，表示资源已经成功获取
	 * 此标志也用于释放资源时重新计算优先级
	 */
	osek_tasks[osek_current_id()].resource_mask |= (1 << ResID);
}

/**
 * 该调用用于在代码中进入临界区，该临界区由<ResID>引用的资源确定。
 * 这样的临界区应当总是通过ReleaseResource来退出。
 * 语法：
 *     StatusType GetResource ( ResourceType <ResID> )
 * 参数（输入）：
 *     ResID			 资源引用
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     请参考  OSEK资源管理优先级上限协议。
 *     仅当内部临界区完全在surrounding临界区内
 *     执行时才允许嵌套资源占用。
 *     针对同一个资源的嵌套占用也是禁止的！
 *     建议对 GetResource 和 ReleaseResource 的
 *     相应调用出现在同一个函数中。
 *     在非抢占任务的重调度点
 *    （TerminateTask，ChainTask, Schedule 以及 WaitEvent），
 *     不允许使用该服务。
 *     另外，在中断服务程序完成之前要离开临界区。
 *     通俗的说，临界应该短小。
 *     该服务可以在ISR和任务级调用
 * 状态：
 *     标准：
 *         无错误，E_OK
 *     扩展：
 *         资源 <ResID> 无效，E_OS_ID
 *         试图获得一个资源，
 *         而该资源已经被某个任务或者ISR获得
 *         或者调用任务（及中断）的静态分配优先级
 *         高于计算的上限优先级，E_OS_ACCESS
 * 一致性：
 *     BCC1, BCC2, ECC1, ECC2
 */
FUNC(StatusType, OS_CODE) GetResource(
	VAR(ResourceType, AUTOMATIC) ResID)
{
	VAR(StatusType, AUTOMATIC) ret = E_OK;
	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR)
#if defined(EXTENDED_STATUS)
	if (ResID >=OS_NR_RESOURCE) {
		ret = E_OS_ID;
		goto out;
	}

	if (osek_tasks[osek_current_id()].resource_mask & (1 << ResID)) {
		ret = E_OS_ACCESS;
		goto out;
	}
#endif

	SuspendAllInterrupts();
	/**
	 * 这里真正执行获取资源的操作
	 */
	__osek_get_resource(ResID);
	/**
	 * 注意，这里可能有重调度
	 */
	ResumeAllInterrupts();

out:
	/**
	 * 如果出现错误，就调用错误钩子
	 * 如果没有配置错误钩子宏，
	 * 编译器会做优化，不用担心。
	 */
	if (ret != E_OK) {
		osek_call_hook_1(GetResource, ret, ResID);
	}

	return ret;
}

static FUNC(void, OS_CODE) __osek_release_resource(
	VAR(ResourceType, AUTOMATIC) ResID)
{
	VAR(int32_t, AUTOMATIC) i;
	VAR(int32_t, AUTOMATIC) prio;

	osek_tasks[osek_current_id()].resource_mask &= ~(1 << ResID);

	prio = osek_tasks[osek_current_id()].prio;
	/**
	 * 重新遍历所有获得的资源
	 * 计算优先级上限
	 */
	for (i = 0; i <OS_NR_RESOURCE; i++) {
		if (osek_tasks[osek_current_id()].resource_mask & (1 << i)) {
			if (prio < osek_resources[i].prio) {
				prio = osek_resources[i].prio;
			}
		}
	}

	/**
	 * 调整当前任务的优先级，确保仍然满足优先级上限
	 * 注意，这里不会进行重调度
	 */
	osek_task_change_prio(osek_current_id(), prio);
}

/**
 * ReleaseResource是与GetResource相对的调用
 * 用于退出由<ResID>引用所确定的代码临界区。
 * 语法：
 *     StatusType ReleaseResource ( ResourceType <ResID> )
 * 参数（输入）：
 *     ResID			资源引用
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     关于嵌套条件的相关信息，参见GetResource。
 *     该服务可以在ISR和任务级调用（参见图12.1）
 * 状态：
 *     标准：
 *        无错误，E_OK
 * 扩展：
 *        资源<ResID>无效，E_OS_ID
 *        试图释放并不由任务或者ISR占据的资源，
 *        或者在此之前需要释放另一个资源E_OS_NOFUNC
 *        试图释放比调用任务或者中断例程静态分配优先级
 *        更低上限优先级的资源，E_OS_ACCESS
 * 一致性：
 *     BCC1, BCC2, ECC1, ECC2
 */
FUNC(StatusType, OS_CODE) ReleaseResource(
	VAR(ResourceType, AUTOMATIC) ResID)
{
	VAR(StatusType, AUTOMATIC) ret = E_OK;
#if defined(EXTENDED_STATUS)
	VAR(uintptr_t, AUTOMATIC) mask;
#endif

	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR)
#if defined(EXTENDED_STATUS)
	if (ResID >=OS_NR_RESOURCE) {
		ret = E_OS_ID;
		goto out;
	}

	mask = osek_tasks[osek_current_id()].resource_mask;
	if ((mask & (1 << ResID)) == 0) { /* 没有申请相应资源 */
		ret = E_OS_NOFUNC;
		goto out;
	}
#endif

	SuspendAllInterrupts();
	/**
	 * 这里真正执行切换操作
	 */
	__osek_release_resource(ResID);
	/**
	 * 注意，这里可能有重调度
	 */
	ResumeAllInterrupts();

out:
	/**
	 * 如果出现错误，就调用错误钩子
	 * 如果没有配置错误钩子宏，
	 * 编译器会做优化，不用担心。
	 */
	if (ret != E_OK) {
		osek_call_hook_1(ReleaseResource, ret, ResID);
	}

	return ret;
}

/**
 * 释放当前任务占用的所有资源
 */
FUNC(void, OS_CODE) osek_release_all_resource(void)
{
	VAR(int32_t, AUTOMATIC) tid;
	VAR(int32_t, AUTOMATIC) i;

	tid = osek_current_id();

	SuspendAllInterrupts();

	for (i = 0; i <OS_NR_RESOURCE; i++) {
		if (osek_tasks[tid].resource_mask & (1 << i)) {
			__osek_release_resource(i);
		}
	}

	ResumeAllInterrupts();
}